#include "HFFVideoPlayer.h"
#include "hlog.h"
#include "confile.h"
#include "avdef.h"
#include "hscope.h"

extern "C"
{
#include "libavutil/avutil.h"
#include "libavutil/pixdesc.h"
#include<libavcodec/avcodec.h>
#include "libswscale/swscale.h"
}


HFFVideoPlayer::HFFVideoPlayer()
{
    hlogi("HFFVideoPlayer::HFFVideoPlayer()");
    sws_ctx = NULL;
    decode_mode = g_confile->Get<int>("decode_mode", "video", DEFAULT_DECODE_MODE);
    HThread::setSleepPolicy(HThread::SLEEP_FOR, 1); 
}

HFFVideoPlayer::~HFFVideoPlayer()
{
    hlogi("HFFVideoPlayer::~HFFVideoPlayer()");
}

bool HFFVideoPlayer::OpenSwsCtx()
{
    int sw, sh, dw, dh;
    sw = codec_ctx->width;
    sh = codec_ctx->height;
    src_pix_fmt = codec_ctx->pix_fmt;

    hlogi("sw=%d sh=%d src_pix_fmt=%d:%s", sw, sh, src_pix_fmt, av_get_pix_fmt_name(src_pix_fmt));
    if (sw <= 0 || sh <= 0 || src_pix_fmt == AV_PIX_FMT_NONE) {
        hloge("Codec parameters wrong!");
        return false;
    }

    dw = sw >> 2 << 2; // align = 4
    dh = sh;
    dst_pix_fmt = AV_PIX_FMT_YUV420P;
    std::string str = g_confile->GetValue("dst_pix_fmt", "video");
    if (!str.empty()) {
        if (strcmp(str.c_str(), "YUV") == 0) {
            dst_pix_fmt = AV_PIX_FMT_YUV420P;
        }
        else if (strcmp(str.c_str(), "RGB") == 0) {
            dst_pix_fmt = AV_PIX_FMT_BGR24;
        }
    }
    hlogi("dw=%d dh=%d dst_pix_fmt=%d:%s", dw, dh, dst_pix_fmt, av_get_pix_fmt_name(dst_pix_fmt));

    sws_ctx = sws_getContext(sw, sh, src_pix_fmt, dw, dh, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
    if (sws_ctx == NULL) {
        hloge("sws_getContext");
        return false;
    }

    hframe.w = dw;
    hframe.h = dh;

    // ARGB
    hframe.buf.resize(dw * dh * 4);

    if (dst_pix_fmt == AV_PIX_FMT_YUV420P) {
        hframe.type = PIX_FMT_IYUV;
        hframe.bpp = 12;
        int y_size = dw * dh;
        hframe.buf.len = y_size * 3 / 2;
        data[0] = (uint8_t*)hframe.buf.base;
        data[1] = data[0] + y_size;
        data[2] = data[1] + y_size / 4;
        linesize[0] = dw;
        linesize[1] = linesize[2] = dw / 2;
    }
    else {
        dst_pix_fmt = AV_PIX_FMT_BGR24;
        hframe.type = PIX_FMT_BGR;
        hframe.bpp = 24;
        hframe.buf.len = dw * dh * 3;
        data[0] = (uint8_t*)hframe.buf.base;
        linesize[0] = dw * 3;
    }

    return true;
}


bool HFFVideoPlayer::Open()
{
    hlogi("HFFVideoPlayer::Open");
    HThread::setSleepPolicy(NO_SLEEP);
    bool ret = true;
    do
    {
        ret = HFFDecode::Open();
        if(!ret)
        {
            hloge("open decoder err!");
            break;
        }

        ret = OpenSwsCtx();
        if(!ret)
        {
            hloge("open SwsCtx err!");
            break;
        }

    }while(0);
    
    return ret;
}



void HFFVideoPlayer::Close()
{
    hlogi("HFFVideoPlayer::Close()");
    HFFDecodeThread::Close();

    if (sws_ctx) {
        sws_freeContext(sws_ctx);
        sws_ctx = NULL;
    }

    hframe.buf.cleanup();
}


void HFFVideoPlayer::doTask()
{
	AVPacket *pkt = Pop();
    if(pkt == NULL)
    {
        return ;
    }

     //音视频同步
    while(!isExit)
    {
        if (synpts >0 && synpts < pts)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            continue;;
        }
        else
        {
            break;
        }
    }

	bool re = Send(pkt);
	if (!re)
	{
		std::this_thread::sleep_for(std::chrono::milliseconds(1));
		return ;
	}

	//一次send 多次recv
	while (!isExit)
	{
		AVFrame * frame = Recv();
		if (!frame) break;
		//显示视频

        defer(XFreeFrame(&frame);)
        int y_size = frame->width * frame->height;

        if (sws_ctx) 
        {
            int h = sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, data, linesize);
            if (h <= 0 || h != frame->height) {
                return;
            }
        }

        if (m_time_base.num && m_time_base.den) 
        {
            hframe.ts = frame->pts / (double)m_time_base.den * m_time_base.num * 1000;
        }
        
        m_onDecodeCallback(m_hp, (void*)(&hframe));
	}
}

bool HFFVideoPlayer::doPrepare()
{
    bool ret = true;
    ret = Open();

    return ret;
}

bool HFFVideoPlayer::doFinish()
{
    Close();
    return true; 
}
